package gao.xioalei.controller;

import gao.xioalei.aware.MyApplicationContextAware;
import gao.xioalei.code.JsonDecoder;
import gao.xioalei.code.JsonEncoder;
import gao.xioalei.dao.MessageDao;
import gao.xioalei.dao.UserOnlineDao;
import gao.xioalei.entity.Message;
import gao.xioalei.entity.UserOnline;
import gao.xioalei.stream.Binder;
import gao.xioalei.util.ServiceName;
import gao.xioalei.util.WebSocketUtil;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Component//如果不注册成bean就无法连接
@ServerEndpoint(value = "/chat/{userId}/{versionUuid}", encoders = {JsonEncoder.class}, decoders = {JsonDecoder.class})
//说明创建websocket的endpoint
//因为webSocket是来一个连接就new一个对象,所以无法依赖注入，所以只能直接自己去容器拿,并且方法上用@Transactional和@StreamListener这些注解也会报错
public class ChatServiceEndPoint {

    //@Resource(name = "UserOnlineDao")
    private UserOnlineDao userOnlineDao = (UserOnlineDao) MyApplicationContextAware.getBean("UserOnlineDao");

    //@Resource(name = "MessageDao")
    private MessageDao messageDao = (MessageDao) MyApplicationContextAware.getBean("MessageDao");

    //@Autowired
    private Environment env = MyApplicationContextAware.getBean(Environment.class);

    //@Autowired
    private RestTemplate restTemplate = MyApplicationContextAware.getBean(RestTemplate.class);

    //@Autowired
    private Binder binder = MyApplicationContextAware.getBean(Binder.class);

    @OnOpen
    //@Transactional
    public void openSession(@PathParam("userId") String userId,@PathParam("versionUuid") String versionUuid, Session session) {
        List<UserOnline> otherSameUser=userOnlineDao.findAllByUserId(userId);//判此用户是否有其他连接
        for(int i=0,length=otherSameUser.size();i<length;i++){
            Message message =new Message();
            message.setType(3);
            if(WebSocketUtil.USERS_ONLINE.containsKey(otherSameUser.get(i).getId()))//如果是在本服务连接的webSocket
                WebSocketUtil.sendMessageToSomeone(otherSameUser.get(i).getId(),message);//向其发送消息让他注销登陆

            else{ //如果是其他服务的话就发消息给对应的服务让其通知用户注销登陆
                message.setOnLineId(otherSameUser.get(i).getId());//让下面复责发消息的websocket服务知道目标用户的OnlineId
                binder.sendMessage(message,otherSameUser.get(i).getRoutingKey());
            }
        }
        //用户的webSocket连接是要将对应的webSocket服务的消息队列RoutIngKey保存到数据库
        userOnlineDao.save(new UserOnline(versionUuid,userId, env.getProperty("spring.cloud.stream.rabbit.bindings.webSocketInput.consumer.bindingRoutingKey"),LocalDateTime.now()));
        //存储用户到服务的hashMap
        WebSocketUtil.USERS_ONLINE.put(versionUuid, session);
        //查看用户是否有未读消息(排序交给前端的话很麻烦)
        List<Message> notReadMessage = messageDao.findAllByReceiverAndIsReadOrderByShowOrderAsc(userId, false);
        for (int i = 0, length = notReadMessage.size(); i < length; i++) {
            String senderName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/realName/").concat(notReadMessage.get(i).getSender()), String.class);
            notReadMessage.get(i).setSenderName(senderName);//设置发件人昵称
            notReadMessage.get(i).setType(0);//判断这是聊天信息
            WebSocketUtil.sendMessage(session, notReadMessage.get(i));//将未读消息直接发送给用户(此时不需要经过消息队列)
        }
    }

    @OnClose
    //@Transactional
    public void closeSession(@PathParam("versionUuid") String versionUuid, Session session) {
        userOnlineDao.deleteById(versionUuid);//将此用户数据库里的websocket的连接信息删除，表示此用户下线
        //删除用户在此服务的信息
        WebSocketUtil.USERS_ONLINE.remove(versionUuid);
        //下线后关闭session
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    //@Transactional(isolation = Isolation.READ_COMMITTED)//防止查询用户是否在线的时候用户刚好状态改为下线了却因为隔离级别为可重复读而导致以为没下线
    public void onMessage(@PathParam("userId") String userId,@PathParam("versionUuid") String versionUuid, Message message) {
        if (message.getType() == 0) {//如果是正常消息
            message.setShowOrder(messageDao.getMaxOrderBySenderAndReceiver(userId, message.getReceiver()) + 1);
            message.setTime(LocalDateTime.now());
            message.setRead(false);
            messageDao.save(message);//将消息保存到数据库
            Optional.ofNullable(userOnlineDao.findTopByUserIdOrderByOnLineTimeDesc(message.getReceiver())).ifPresent(receiver -> {//如果用户在线就将消息发到对应用户所在webSocket的服务的消息队列中
                message.setOnLineId(receiver.getId());//让下面复责发消息的websocket服务知道目标用户的OnlineId
                binder.sendMessage(message, receiver.getRoutingKey());
            });
        } else if (message.getType() == 1) { //如果是通知服务器消息已读
            messageDao.findById(message.getId()).ifPresent(msg -> {
                msg.setRead(true);
                messageDao.save(msg);
            });
            //messageDao.updateIsRead(true, message.getId());这条更新语句一定要加事务,但是现在这里无法加事务
        } else if (message.getType() == 2) {//如果是获取历史消息
            List<Message> historyMsg = null;
            if (message.getId() == null)//如果没有Id的话说明是从最新的消息拿
                historyMsg = messageDao.findAllByReceiverAndSenderOrReceiverAndSenderOrderByShowOrderDesc(message.getReceiver(), message.getSender(), message.getSender(), message.getReceiver(), PageRequest.of(0, 5)).stream().collect(Collectors.toList());
            else //如果有Id的话说明是从某个的消息前面拿
                historyMsg = messageDao.findAllByReceiverAndSenderOrReceiverAndSenderAndShowOrderBeforeOrderByShowOrderDesc(message.getReceiver(), message.getSender(), message.getSender(), message.getReceiver(), messageDao.getShowOrderById(message.getId()), PageRequest.of(0, 5, Sort.by(Sort.Order.desc("showOrder")))).stream().collect(Collectors.toList());
            for (int i = 0, length = historyMsg.size(); i < length; i++) {
                historyMsg.get(i).setType(2);
                WebSocketUtil.sendMessageToSomeone(versionUuid, historyMsg.get(i));//将历史消息直接发送给用户(此时不需要经过消息队列)
            }
        }

    }

    @OnError
    public void sessionError(Session session, Throwable throwable) {
        throwable.printStackTrace();
        System.out.println("WebSocket连接发生异常，message:" + throwable.getMessage());
    }

}
